#include "windowwidget.h"
#include "ui_windowwidget.h"

#include <iostream>
#include <fstream>

/*
 Problems:
    -camera movement incomplete
    -file dialog not appearing on top of full screen mode
    -switch to maximized messed up menu spacing

 menu:
    -create: goto blank map
    -load: open window, ask for file, load in and go to map mode, and message "Loaded"
    -save: save initial config of current map, goto map mode, and message "Saved"
    -run: go to 3d mode and message if no scenario contents...
    -speed: change running speed of scenario
    -map: goto overhead view, and pause?
 */
/*
 controls:
    -menu
        -enter: save, load, create, run, map
    -3d mode:
        -R for rotae translate switch...maybe change
        -WASD/Arrow keys
        -esc: menu
    -map mode:
        -esc: menu
 */

//creates the window for the game, and begins initialization:
////initializeGL is called first////
windowWidget::windowWidget(QWidget *parent) :
    QGLWidget(parent),
    ui(new Ui::windowWidget)
{   //this uses the ui file to set up the window
    ui->setupUi(this);
    //This sets the timer to run forever, not just once
    timer.setSingleShot(false);
    //This connects the timer to a function that will be called whenever it goes off: basically a callback function
    connect(&timer, SIGNAL(timeout()), this, SLOT(animate()));
    //Begins the timer with 33 milliseconds
    timer.start(33);
}

//destroys window and contents//
windowWidget::~windowWidget()
{
    delete ui;
}

//Handles mouse press events: a callback function called whenever a mouse button is pressed//
void windowWidget::mousePressEvent(QMouseEvent *event){
    /*Did this as quick fix for problem with dual monitors.
      If we need the mouse, we'll need to remove this*/
    QCursor::setPos(0,0);             //default mouse position
}

//callback called whenever the mouse is moved
void windowWidget::mouseMoveEvent(QMouseEvent *event){

}

//callback for whenever a mouse button is released
void windowWidget::mouseReleaseEvent(QMouseEvent *event){

}

//callback for whenever a keyboard key is pressed
void windowWidget::keyPressEvent(QKeyEvent *event){
    if(playMode==0){//if in Menu mode
        switch(event->key()){//get key input
        case Qt::Key_Escape://If Escape
            exit(1);        //exit program
            break;
        case Qt::Key_Down:  //if down arrow pressed
            //increment menu postion
            if(menuSel<5){//if not bottom of menu
                menuSel++;
            }
            else{         //else reset to top
                menuSel=0;
            }
            break;
        case Qt::Key_Up: //if up arrow pressed
            //decrement menu position
            if(menuSel>0){//if not top of menu
                menuSel--;
            }
            else{         //else set to bottom
                menuSel=5;
            }
            break;
        case Qt::Key_Right: //if right arrow pressed
            //Select options: slider menu (speed only for now)
            if(menuSel==5){//if on speed menu option
                if(speedChoice<3){//increase if under 3
                    speedChoice++;
                }
                else{             //set to low if at 3
                    speedChoice=0;
                }
            }
            break;
        case Qt::Key_Left: //if left arrow pressed
            //Select options: slider menu (speed only for now)
            if(menuSel==5){ //if on speed menu option
                if(speedChoice>0){ //decrease if not at zero
                    speedChoice--;
                }
                else{             //else set to bottom
                    speedChoice=3;
                }
            }
            break;
        case Qt::Key_Return: //if return pressed
            if(menuSel==0){//create map selected
                //clear current data
                /*INCOMPLETE*/
                //go to map mode
                playMode=2;
            }
            else if(menuSel==1){//load option selected
                //load function:
                loadScenario();
                //go to map mode for adjustments, show "loaded" text
                playMode=2;
            }
            else if(menuSel==2){//save option selected
                //save function
                saveScenario();
                //go to map mode again, show "saved" text
                playMode=2;
            }
            else if(menuSel==3){//run scenario
                //go to 3-D mode and unpause
                /*INCOMPLETE: check transition*/
                playMode=1;
            }
            else if(menuSel==4){//map mode option
                //go to the map and pause
                playMode=2;
            }
            else{// if(menuSel==5){//speed
                //nothing, speed not enterable...
            }
            break;
        default:
            break;
        }
    }
    else if(playMode==1){//active mode
        switch(event->key()){//get key pressed
        case Qt::Key_Escape://If esc, go to menu
            playMode=0;
            break;
        case Qt::Key_Up:    //If up arrow pressed
          //std::cout << "Moving forward." << std::endl; //qDebug()<<"Moving Forward.";
            myCamera.moveForward();   //camera move forward
            break;
        case Qt::Key_Left: //left arrow pressed
          //std::cout << "Moving left." << std::endl;
            myCamera.moveLeft();     //camera move left
            break;
        case Qt::Key_Right: //right arrow pressed
          //std::cout << "Moving right." << std::endl;
            myCamera.moveRight();    //camera move right
            break;
        case Qt::Key_Down: //down arrow pressed
          //std::cout << "Moving back." << std::endl;
            myCamera.moveBack();    //camera move backward
            break;
        case Qt::Key_PageUp:       //page up pressed
          //std::cout << "Moving up." << std::endl;
            myCamera.moveUp();    //camera move up
            break;
        case Qt::Key_PageDown:    //page down pressed
          //std::cout << "Moving down." << std::endl;
            myCamera.moveDown();  //camera moved down
            break;
        case Qt::Key_R:          //R pressed
          //std::cout << "Toggling rotation." << std::endl;
            myCamera.toggleRotating();//switch camera mode: rotate/translate
	  break;
        default:
          //std::cout << "Nothing going on..." << std::endl;
            break;
        }
    }
    else{//map mode
        switch(event->key()){ ///get key event
        case Qt::Key_Escape://go to menu on escape
            playMode=0;
            break;
        case Qt::Key_Up:     //if up key pressed
          //std::cout << "Should be moving forward." << std::endl;
            myCamera.moveForward(); //move camera forward
            break;
        case Qt::Key_Left:    //left key pressed
          //std::cout << "should be moving left." << std::endl;
            myCamera.moveLeft();  //move camera left
            break;
        case Qt::Key_Right:  //right key pressed
          //std::cout << "Should be moving right. " << std::endl;
            myCamera.moveRight(); //move camera right
            break;
        case Qt::Key_Down:      //down key pressed
          //std::cout << "Should be moving back. " << std::endl;
            myCamera.moveBack(); //move camera back
            break;
        case Qt::Key_PageUp:    //up key pressed
            myCamera.moveUp();  //move camera up
            break;
        case Qt::Key_PageDown:  //down key pressed
            myCamera.moveDown();//move camera down
            break;
        case Qt::Key_R:        //R pressed
          //std::cout << "Toggling rotating." << std::endl;
          myCamera.toggleRotating();//switch camera translate/rotate mode
	  break;
        default:
            break;
        }
    }
    updateGL();                //update image after changes: without this we will see nothing changing.
}

//set up the GL world for viewing
void windowWidget::initializeGL(){
    //Initialize variables of class, as in constructor
    //showFullScreen();  //sometimes broken, interferes with mouse motion and menu centering
    showMaximized();     //somewhat better screen size
    //set menu variables
    playMode=0;//in Menu
    worldLoaded=0;//have not loaded or created world with terrain and ships
    subMenu=0;//no sub menu
    menuSel=0;//first item
    initMenuX=menuX=width()/2;  //create menu spacing based on screen size
    initMenuY=menuY=150;        //same here as above
    yInc=60;
    speedChoice=0;              //speed currently paused/slow

    //load meshes (if default)

    ///Terrain prep here (if default)

    /////////////////////////////

    glClearColor(0.0, 0.0, 0.0, 1.0); //sets background /default window color
    glMatrixMode(GL_PROJECTION);      //sets viewing mode
    glLoadIdentity();                 //loads a default position in world, basically the origin

    glEnable(GL_DEPTH_TEST);          //allows the program to ignore objects behind other objects: less wasted time

    float w = (float)width();         //variables for window shape: used in resizing and menus
    float h = (float)height();

    gluPerspective(45.0, w/h, 1.0, 10000.0);//sets view range and form

    //lighting
    glEnable(GL_COLOR_MATERIAL);            //allows for combining of textures and colors applied to meshes
    //enables coloring of both sides of a shape, and interaction with ambient and diffuse light
    glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
    //set up world view type
    glMatrixMode(GL_MODELVIEW);             //Set type of view
    glLoadIdentity();                       //loads a default position in world, basically the origin
    //lighting continued
    sceneLights.turnOnLights();             //initialize lights (make sure you can see

    //set up camera
    myCamera.setUp();                       //initialize camera (so it is in a good place)
}

//a function called to refresh the world: this redraws everything with your changes, otherwise its jsut a screenshot
void windowWidget::paintGL(){
    glClearColor(0.0,0.0,0.0,1.0);            //set clear color to white
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //clear everything you've already drawn
    //create fonts...can't do in header...
    QFont serifFont("Times", 30);//create font 1   //Options: ar destine, papyrus
    QFont serifFont2("ar destine", 50, QFont::Bold);//font 2
    if(playMode==0){//menu draw
        glColor3f(0.2,1.0,0.0); //set text color to greenish
        renderText(menuX-200,90,"Ship Sim", serifFont2); //print simulator name NEED NEW NAME
        if(menuSel==0){//check if Create is selected
            glColor3f(0.2,1.0,0.0);  //set selected color (bright green)
        }
        else{
            glColor3f(0.0,0.5,0.0);//if not set deselected color (dark green)
        }
        renderText(menuX-100,menuY,"Create", serifFont); //print Create option
        if(menuSel==1){//load
            glColor3f(0.2,1.0,0.0);//set selected color (bright green)
        }
        else{
            glColor3f(0.0,0.5,0.0);//if not set deselected color (dark green)
        }
        renderText(menuX-100,menuY+yInc*1,"Load", serifFont); //print Load option
        if(menuSel==2){//save
            glColor3f(0.2,1.0,0.0);//set selected color (bright green)
        }
        else{
            glColor3f(0.0,0.5,0.0);//if not set deselected color (dark green)
        }
        renderText(menuX-100,menuY+yInc*2,"Save", serifFont); //print Save option
        if(menuSel==3){//run
            glColor3f(0.2,1.0,0.0);//set selected color (bright green)
        }
        else{
            glColor3f(0.0,0.5,0.0);//if not set deselected color (dark green)
        }
        renderText(menuX-100,menuY+yInc*3,"Run", serifFont); //print Run option
        if(menuSel==4){//map
            glColor3f(0.2,1.0,0.0);//set selected color (bright green)
        }
        else{
            glColor3f(0.0,0.5,0.0);//if not set deselected color (dark green)
        }
        renderText(menuX-100,menuY+yInc*4,"Map", serifFont); //print Map option
        if(menuSel==5){//speed
            glColor3f(0.2,1.0,0.0);//set selected color (bright green)
        }
        else{
            glColor3f(0.0,0.5,0.0);//if not set deselected color (dark green)
        }
        if(speedChoice==0){        //check speed selection
            renderText(menuX-100,menuY+yInc*5,"< Speed: 1 >", serifFont);//print current speed
        }
        else if(speedChoice==1){
            renderText(menuX-100,menuY+yInc*5,"< Speed: 2 >", serifFont);
        }
        else if(speedChoice==2){
            renderText(menuX-100,menuY+yInc*5,"< Speed: 3 >", serifFont);
        }
        else{
            renderText(menuX-100,menuY+yInc*5,"< Speed: 4 >", serifFont);
        }
    }
    else if(playMode==1){//active

      glClearColor(135.0/256.0, 206.0/256.0, 250.0/256.0, 1.0);

      if(worldLoaded==1){
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	// apply camera transform
	myCamera.applyTransform();

	// draw terrain
        glColor3f(0.0,0.0,1.0);
        glBegin(GL_POLYGON);
          glVertex3f(2000.0, 0, 2000.0);
          glVertex3f(-2000.0, 0, 2000.0);
          glVertex3f(-2000.0, 0, -2000.0);
          glVertex3f(2000.0, 0, -2000.0);
        glEnd();

	// draw the boats
	for (int i = 0; i < 10; ++i) {
	  glPushMatrix();
          boat temp;
          temp=fleet.front();
            //iterate through fleet, draw each at current position...
          glTranslatef(temp.current[0], 0, temp.current[1]);
            //draw
          //temp.obj->draw();
	  glPopMatrix();
	}
	
      } else {
	renderText(menuX-100,menuY,"No scenario loaded", serifFont);
      }

    }
    else{//map
        if(worldLoaded==1){ //if world loaded:view from above? limit camera movment
            /*INCOMPLETE*/

          glClearColor(135.0/256.0, 206.0/256.0, 250.0/256.0, 1.0); //set background color
          glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);       //clear previous drawing
	  
          glMatrixMode(GL_MODELVIEW);                               //fix view params
          glLoadIdentity();                                         //reset world matrix

	  // apply camera transform
          myCamera.applyTransform();                                //set camera to current position

	  // draw terrain

          glColor3f(0.0,0.0,1.0);                                  //set draw color to blue
          glBegin(GL_POLYGON);                                     //draw the ocean. basically a rectangle of blue.
            glVertex3f(2000.0, 0, 2000.0);
            glVertex3f(-2000.0, 0, 2000.0);
            glVertex3f(-2000.0, 0, -2000.0);
            glVertex3f(2000.0, 0, -2000.0);                       /*might be nice to have this follow the camera,
                                                                   so there is no edge of the world. */
          glEnd();

          // draw the boats
          for (int i = 0; i < fleet.size(); ++i) {                //iterate through boat list
            glPushMatrix();                                       //store the world
            boat temp;
            temp=fleet[i];
              //iterate through fleet, draw each at current position...
            glTranslatef(fleet[i].current[0], 0, fleet[i].current[1]);  //move to boat position
              //draw
            if (fleet[i].danger) {                                     //draw boat
                fleet[i].obj->color[0] = 1.0;
                fleet[i].obj->color[1] = 0.0;
                fleet[i].obj->color[2] = 0.0;
            } else {
                fleet[i].obj->color[0] = 0.2;
                fleet[i].obj->color[1] = 0.2;
                fleet[i].obj->color[2] = 0.2;
            }

            if (!fleet[i].colliding)
              fleet[i].obj->draw();
            glPopMatrix();                                              //return to old world position
          }
        }
        else{
            glColor3f(1.0,1.0,1.0);                                    //print error: no world
            renderText(menuX-100,menuY,"No scenario loaded", serifFont);
        }

    }

}

//callback Used for resizing the window if you drag the corner
void windowWidget::resizeGL(int width, int height){
    //showFullScreen();
    showMaximized();  //we won't let you resize it: returns to default so we don't need to resize ships
    glViewport(0, 0, width, height);             //reset viewport: realy only needed if you made changes
}

//sets up the viewport (window): default view settings
void windowWidget::setupViewport(int width, int height){
    glMatrixMode(GL_PROJECTION);                                //set up default view settings
    glLoadIdentity();
    gluPerspective(45, (float)width/(float)height, 1.0, 400.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

//checks for collisions: code is currently in animate, maybe move
void windowWidget::checkCollisions(){

}

//instant reset may not be necessary
void windowWidget::resetGame(){

}

//AI should always be active for opponent ships, never be active for ally, may not need this
void windowWidget::autoPlay(){

}

//Load all information from a file: terrain, then ships; ships: name, mesh, list of way points
void windowWidget::loadScenario(){//-load //function: file dialog
    //open file dialog(allows file selection in traditional windows way)
    QString fileName = QFileDialog::getOpenFileName(this,
         tr("Open Scenario"), "scenarios/", tr("Scenario Files (*.scn)"));//check if canceled?
    //read in file name
    std::string file_name = fileName.toStdString();
    //open file
    std::cout << "Opening: " << file_name << std::endl;
    //mesh loader create and use
    //temp variables for storing file contents
    std::string currentLine; //read whole line into here
    int init[2];             //hold positions
    boat temp;              //hold each boat
    size_t first, last;     //for use in parsing: string locations
    std::vector<std::string> v; //
    std::ifstream file(file_name.c_str());//open file

    // load terrain
    getline(file, currentLine);   //read current line
    //Terrain first
    std::cout << "Loading terrain model: " << currentLine << std::endl;
    //create terrain loader

    //use terrain loader

    worldLoaded = 1; //set bool check

    //while reading works, keep getting lines
    while (getline(file, currentLine)) {//getline(file, currentLine)
      // load i-th model
      // set mesh
            //load model name string (*.obj)
      first = currentLine.find(" ");
      temp.mesh = currentLine.substr(0,first);
      //load mesh into boat obj

        temp.obj->color[0]=0.2;
        temp.obj->color[1]=0.2;
        temp.obj->color[2]=0.2;
        //qDebug()<<QString::fromStdString(temp.obj->toString());
      //qDebug()<<QString::fromStdString(temp.mesh);
      // set name/number
            //load ship name
      last = currentLine.find(" ",first+1);//have first space and next
      //qDebug()<<int(first)<<' '<<int(last);
      temp.name = currentLine.substr(first, last-first);
      //qDebug()<<QString::fromStdString(temp.name);
      //qDebug()<<int(first)<<' '<<int(last);
      // set position
            //get coordnates (2d)
      first = currentLine.find("(");
      last = currentLine.find(",",first);
      //qDebug() << currentLine.substr(first+1, last-first).c_str();
      init[0] = atoi(currentLine.substr(first+1,last-first).c_str());
      first = last;
      last = currentLine.find(")",first);
      //qDebug() << currentLine.substr(first, last-first).c_str();
      init[1] = atoi(currentLine.substr(first+1,last-first).c_str());
      temp.positions.push_back(init);
      temp.current[0]=init[0];
      temp.current[1]=init[1];
      //qDebug()<<"currentPos"<<init[0]<<','<<init[1];
      // set waypoints
      size_t fin = currentLine.find_last_of(")");
      //qDebug()<<int(fin);
      while(last!=fin){
                first = currentLine.find("(", last);
                last = currentLine.find(",",first);
                if(last!=size_t(-1)){
                    init[0] = atoi(currentLine.substr(first,last-first).c_str());
                    first = last;
                    last = currentLine.find(")",first);
                    init[1] = atoi(currentLine.substr(first,last-first).c_str());
                    temp.positions.push_back(init);
                    //qDebug()<<"currentPos"<<init[0]<<','<<init[1];
                }
            }
      //add boat to fleet
      fleet.push_back(temp);
    //qDebug() << "Pushed a ship";
  }
}

//Save all information to file: terrain then ships; ships: name, mesh, list of way points
void windowWidget::saveScenario(){//-save //function: file dialog
    QString fileName = QFileDialog::getSaveFileName(this,
         tr("Save Scenario"), "scenarios/", tr("Scenario Files (*.scn)"));//check if canceled?
    std::string file_name = fileName.toStdString();

    std::cout << "Opening: " << file_name << std::endl;

    std::ofstream file(file_name.c_str());
    //store terrain file name

    //store boats: mesh, name, positions

}

//checks collisions, updates ship status, animates?
void windowWidget::animate() {
  
  // check for collisions/update physics
    for (int i = 0; i < fleet.size(); ++i) {
        for (int j = 0; j < fleet.size(); ++j) {
            if(i == j)
                continue;
            float x_diff = fleet[i].current[0] - fleet[j].current[0];
            float z_diff = fleet[i].current[1] - fleet[j].current[1];

            if (x_diff*x_diff + z_diff*z_diff < 50) {
                fleet[i].danger = true;
            }

            if (x_diff*x_diff + z_diff*z_diff < 16) {

                //qDebug() << "Collision! Between " << i << " " << j;
                fleet[i].setColliding(true);
                fleet[j].setColliding(true);
            }
        }
    }

    // check for boat/land collisions
    for (int i = 0; i < fleet.size(); ++i) {
        int x = fleet[i].current[0];
        int z = fleet[i].current[1];
        /*if (terrain->data[x * terrain->height + z] > 3 &&
            terrain->data[x * terrain->height + z] < 256) {
            //qDebug() << "Beached!" << terrain->data[x * terrain->height + z];
        }*/
    }

  // update positions
    int mult;
    for (int i = 0; i < fleet.size(); ++i) {
        if (i % 2 == 0) {
          mult = 1;
        } else {
            mult = -1;
        }
     fleet[i].current[0] +=  mult * 0.05;
     fleet[i].current[1] += mult * 0.05;
    }

  // draw the world
  updateGL();

}
